#include "mm.h"
#include "kernel.h"
#include "assert.h"
#include "string.h"

#define ARDS_ADDR 0x7e00

#define VALID_MEMORY_FROM 0x100000

#define PHY_MEMORY_USE_FROM 0xa00000

#define ZONE_VALID 1 // 内存可用区域
#define ZONE_RESERVED 2

static physics_memory_info_t g_physics_memory;

static physics_memory_map_t g_physics_memory_map;

void phy_memory_init()
{
    check_memory_info_t *info = (check_memory_info_t *) ARDS_ADDR;
    check_memory_item_t *item = (check_memory_item_t *) (ARDS_ADDR + sizeof(u16));

    for (int i = 0; i < info->count; i++)
    {
        check_memory_item_t *p = &item[i];
        if (p->base_addr_low > 0 && p->type == ZONE_VALID) 
        {
            g_physics_memory.addr_start = p->base_addr_low;
            g_physics_memory.valid_mem_size = p->length_low;
            g_physics_memory.addr_end = p->base_addr_low + p->length_low;
        }
    }
    
    if (VALID_MEMORY_FROM != g_physics_memory.addr_start) 
    {
        printk("no valid memory found\n");
        return;
    }
    
    g_physics_memory.pages_total = g_physics_memory.valid_mem_size / PAGE_SIZE;
    g_physics_memory.pages_used = 0;
    g_physics_memory.pages_free = g_physics_memory.pages_total - (PHY_MEMORY_USE_FROM / PAGE_SIZE);

    g_physics_memory_map.addr_base = PHY_MEMORY_USE_FROM;
    g_physics_memory_map.bitmap_buf = (u8 *) 0x500;
    g_physics_memory_map.pages_total = g_physics_memory.pages_free;

//    printk("total pages:%x\n", g_physics_memory.pages_total);

    u32 bitmap_len = g_physics_memory_map.pages_total / 8;
    bitmap_len += (g_physics_memory_map.pages_total % 8) ? 1 : 0;
    memset(g_physics_memory_map.bitmap_buf, 0, bitmap_len);
//    printk("bitmap length:%x\n", bitmap_len);
//    printk("the end of bitmap:%x\n", 0x500 + bitmap_len);

    bitmap_make(&g_physics_memory_map.bitmap, g_physics_memory_map.bitmap_buf, g_physics_memory_map.pages_total, 0);
}

void* get_free_page()
{
    if (g_physics_memory.pages_free <= 0)
    {
        return NULL;
    }
    int page = bitmap_scan(&g_physics_memory_map.bitmap, 1);
    assert(page != EOF);
    bitmap_set(&g_physics_memory_map.bitmap, page, true);
    return g_physics_memory_map.addr_base + (page << 12);
}

void free_page(void* addr)
{
    int index = ((u64) addr - g_physics_memory_map.addr_base) / PAGE_SIZE;
    assert(index < g_physics_memory_map.bitmap.length);
    bitmap_set(&g_physics_memory_map.bitmap, index, false);
}


void print_check_memory_info()
{
    check_memory_info_t *info = (check_memory_info_t *) ARDS_ADDR;
    check_memory_item_t *item = (check_memory_item_t *) (ARDS_ADDR + sizeof(u16));

    u16 count = info->count;

    printk("====== memory check info =====\n");

    for (size_t i = 0; i < count; i++)
    {
        check_memory_item_t* p = &item[i];
        printk("\t %x, %x, %x, %x, %d\n", p->base_addr_high, p->base_addr_low,
               p->length_high, p->length_low, p->type);
    }
    
    printk("====== memory check info =====\n");
}